#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
"""
Statistics Formatting
=====================

Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2003 Mt. Hood Playing Card Co.
Copyright (C) 2005-2010 Skomoroh
Copyright (C) 2010- Jeremy Austin-Bardo

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
"""
from datetime import datetime, timedelta
from .utillib.misc import format_time
from .gamelib.data import GAME_DB
from .gamelib import group as GI
from .statdb import STAT_DB
from .utillib.pathdb import NJ_PATH, PATH_DB

class StatsFormatter(object):
    __game_stats = []
    __game_avg = {'time': format_time(0), 'move': 0, 'win': 0}
    __game_sum = {'all': 0, 'play': 0, 'win': 0, 'lose': 0}

    def __init__(self, player, gameid):
        percent = lambda w, t: 100.0*w/t if t else 0.0

        twon, tlost, ttime, tmoves, tgames = 0, 0, timedelta(), 0, 0
        name = []

        for id_ in GAME_DB.sort('name'):
            gi = GAME_DB.get[id_]['id']
            gid = gi['id']
            won, lost, time, moves = STAT_DB.full(player, gid)
            gamename = gi['name']
            if won > 0 or lost > 0 or id_ == gameid:
                if gamename in name:
                    continue
                else:
                    name.append(gamename)
                twon, tlost = twon + won, tlost + lost
                ttime, tmoves = ttime+time, tmoves+moves

                self.__game_stats.append({'name': gamename, 'moves': moves,
                                      'played': won+lost, 'won': won,
                                      'lost': lost, 'time': format_time(time),
                                      'percent': percent(won, won+lost),
                                      'gid': gid})
                tgames +=1

        #{ summary
        if tgames > 0:
            self.__game_avg['time'] = format_time(ttime//tgames)
            self.__game_avg['move'] = tmoves/tgames

        self.__game_avg['win'] = "%.1f"%percent(twon, tgames)+"%"
        self.__game_sum['all'] = len(GAME_DB)
        self.__game_sum['play'] = tgames
        self.__game_sum['win'] = twon
        self.__game_sum['lose'] = tlost

    def getResults(self, sort_by='name'):
        #self.__game_stats.sort(key=sort_by)
        for items in sorted(self.__game_stats, key=lambda i: i[sort_by]):
            yield (items['name'], items['played'], items['won'],
                   items['lost'], items['moves'], items['time'],
                   "%.1f"%items['percent']+"%", items['gid'])

    def getSummary(self):
        return (self.__game_sum['all'], self.__game_sum['play'],
                self.__game_sum['win'], self.__game_sum['lose'],
                self.__game_avg['move'], self.__game_avg['time'],
                self.__game_avg['win'])


class LogFormatter(object):
    __gstate = [_("Lost"), _("Won"), _("Perfect"), _("Loaded"), _("Not won")]

    def getLog(self, all_):
        err_msg = n_("***") + _("ERROR") + n_("***")
        for pg in STAT_DB.games('all', all_=all_):
            if not isinstance(pg, dict):
                continue
            if not any([v in pg for v in ('gid', 'num', 'sdate', 'state')]):
                continue
            #{ Get name of game in log record
            gameid = pg['gid']
            gi = GAME_DB[GAME_DB.find(gameid, 'id')]
            name = gi.name if gi else _("** UNKNOWN %d **") % gameid
            #{ Get number of game in log record
            number = pg['number'] if len(str(pg['number'])) <= 20 else err_msg
            #{ Get date of game in log record
            date = str(pg.get('sdate'))
            #{ Get state of game in log record
            state = (self.__gstate[pg['state']]
                     if -3 < pg['state'] or pg['state'] > 3 else err_msg)

            yield [name, number, date, state, pg['state'], gameid]


class FileStatsFormatter(StatsFormatter):

    def __init__(self, file_):
        self.file = file_

    def p(self, s):
        self.file.write(s.encode('utf-8'))

    def nl(self, count=1):
        self.p("\n" * count)

    def pheader(self, s):
        self.p(s)

    def pstats(self, *args, **kwargs):
        s = "%-30s %7s %7s %7s %7s %7s %7s\n" % args
        self.p(s)

    def plog(self, gamename, gamenumber, date, status, gameid=-1, won=-1):
        self.p("%-25s %-20s %17s  %s\n" % (gamename, gamenumber, date,
                                           status))

    def writeHeader(self, header, pagewidth=72):
        import time
        date = time.ctime(time.time())
        date = time.strftime("%Y-%m-%d  %H:%M", time.localtime(time.time()))
        blanks = max(pagewidth - len(header) - len(date), 1)
        self.pheader(header + " "*blanks + date + "\n")
        self.pheader("-" * pagewidth + "\n")
        self.pheader("\n")

    def writeStats(self, player, sort_by='name'):
        player = _('Demo') if player is None else player
        header = _("Statistics for ") + player
        self.writeHeader(header, 62)
        header = self.getStatHeader()
        self.pstats(*header)
        self.nl()
        for result in self.getStatResults(player, sort_by):
            gameid = result.pop()
            self.pstats(gameid=gameid, *result)
        self.nl()
        total, played, won, lost, time, moves, perc = self.getStatSummary()
        self.pstats(_("Total (%d out of %d games)") % (played, total),
                      won+lost, won, lost, time, moves, perc)
        self.nl(2)
        return played

    def writeLog(self, player, header, games):
        if not player or not games:
            return 0
        self.writeHeader(header, 71)
        header = self.getLogHeader()
        self.plog(*header)
        self.nl()
        for result in self.getLogResults(player, games):
            gameid = result.pop()
            won = result.pop()
            self.plog(gameid=gameid, won=won, *result)
        self.nl(2)
        return 1

    def writeFullLog(self, player):
        player = _('Demo') if player is None else player
        header = _("Full log for ") + player
        games = STAT_DB.allgames()
        return self.writeLog(player, header, games)

    def writeSessionLog(self, player):
        return self.writeFullLog(self, player)


class ProgressionFormatter:
    __all = {} #: key: (year, month, day);  value: [played, won]
    __cur = {} #: key: (year, month, day);  value: [played, won]
    _vars = {} #: key: name; val: TkVariable
    __KF = lambda s, dt: dt.strftime('%Y%m%d')

    def __init__(self, player, gameid):
        games = STAT_DB.games('all')
        if not games:
            return
        for g in games:
            id = g.get('gid')
            status = g.get('state')
            start_time = self.__KF(g.get('sdate'))
            if start_time not in self.__all:
                self.__all[start_time] = [0, 0]
            self.__all[start_time][0] += 1
            if status > 0:
                self.__all[start_time][1] += 1
            if id == gameid:
                if start_time not in self.__cur:
                    self.__cur[start_time] = [0, 0]
                self.__cur[start_time][0] += 1
                if status > 0:
                    self.__cur[start_time][1] += 1

    def _getResults(self, interval, all_):
        results = (self.__all if all_ == 'all' else self.__cur)

        now = datetime.now()

        if interval == 'week':
            return self.__getValues(lt=now-timedelta(days=7), nt=now,
                                    marks=None, delta=1, format='%a',
                                    results=results)

        elif interval == 'month':
            return self.__getValues(lt=now-timedelta(days=30), nt=now,
                                marks=range(28, 0, -7), delta=1, format='%d',
                                results=results)

        elif interval == 'year':
            return self.__getValues(lt=now-timedelta(days=365), nt=now,
                              marks=range(360, 0, -30), delta=7, format='%b',
                                   results=results)

        else:                           # all
            if results:
                lt = datetime.strptime(min(results.keys()), '%Y%m%d')
            else:
                return ('', 0, 0)
            dt = now-lt
            if dt > timedelta(days=365):         # 1 year
                d = dt//12
                marks = []
                while now > lt:
                    marks.append(self.__KF(lt))
                    lt += d
                if lt.date() != now.date():
                    marks.append(self.__KF(now))
                return self.__getValues(lt=now-timedelta(days=365), nt=now,
                                        marks=marks, delta=28, format='%x',
                                        results=results)
            elif dt > timedelta(days=30):         # 1 month
                return self._getResults('year', all_)
            elif dt > timedelta(days=7):         # 1 week
                return self._getResults('month', all_)
            else:
                return self._getResults('week', all_)

    def __getValues(self, lt, nt, marks, delta, format, results):

        if marks is not None and type(marks[0]) is not str:
            marks = [self.__KF(lt)]+[self.__KF(lt+timedelta(days=o))
                                     for o in marks]+[self.__KF(nt)]

        res = []
        ldt = datetime.now()
        in_delta = delta
        while ldt >= lt:
            played, won = 0, 0
            for i in range(in_delta):
                lct = self.__KF(ldt)
                if lct in results:
                    played += results[lct][0]
                    won += results[lct][1]
                text = ldt.strftime(format)
                ldt -= timedelta(days=1)
                if marks is not None and lct in marks:
                    in_delta = i
                    break
            else:
                in_delta=delta
            if marks is not None and lct not in marks:
                text = None
            res.append((text, played, won))
        res.reverse()
        return res

def save_game_stats(player, mode):
    """
    save_game_stat(player:str, mode:int) -> None

    Call file stats formater attr of mode key and write results to a file.
    Calls dialog error or info depending on the write success.
    """
    from .viewlib.dialog import Dialog
    name, write_method = {202: ('stats', 'writeStats',),
                          203: ('log', 'writeFullLog',),
                          204: ('log', 'writeSessionLog',)}[mode]
    text = _('Your statistics')
    if player is _('Demo'):
        text = _('Demo statistics')
        name += '_demo'

    name = NJ_PATH([PATH_DB.dn['user'], '{}.txt'.format(name)])
    try:
        with open(name, 'a') as file_:
            formatter = FileStatsFormatter(file_)
            func = getattr(formatter, write_method)
            func(player)
    except EnvironmentError as err:
        Dialog.error(_('Error writing to file.'), err)
    else:
        Dialog.info(_('{0} appended to {1}').format(text, name))